Android audio音频流数据异常问题分析 您所在的位置:网站首页 bose c5声音忽大忽小 Android audio音频流数据异常问题分析

Android audio音频流数据异常问题分析

2023-09-12 14:04| 来源: 网络整理| 查看: 265

一、背景

在 Android 系统的开发过程当中,音频异常问题通常有如下几类,无声,调节不了声音,爆音,声音卡顿,声音效果异常(忽大忽小,低音缺失等)等。尤其声音效果这部分问题通常从日志上信息量较少,相对难定位根因。想要分析此类问题,便需要对声音传输链路有一定的了解,能够在链路中对各节点的音频流进行采集,通过对比分析音频流的实际效果来缩小问题范围,找出原因。 网上已经有很多音频框架图和相关的大致介绍,这里就不再赘述,只分享下音频流的传输链路,和我们可以重点其中的哪些关键节点,来帮助我们快速定位问题。

二、Android Audio 音频系统 1. 音频链路

抓取音频链路当中的音频数据是分析声音异常问题的有效方法,通过抓取不同节点的声音数据,可以帮助我们快速定位问题发生的原因。下面先来看一张安卓官方的音频系统框架图:

一、背景

在 Android 系统的开发过程当中,音频异常问题通常有如下几类,无声,调节不了声音,爆音,声音卡顿,声音效果异常(忽大忽小,低音缺失等)等。尤其声音效果这部分问题通常从日志上信息量较少,相对难定位根因。想要分析此类问题,便需要对声音传输链路有一定的了解,能够在链路中对各节点的音频流进行采集,通过对比分析音频流的实际效果来缩小问题范围,找出原因。 网上已经有很多音频框架图和相关的大致介绍,这里就不再赘述,只分享下音频流的传输链路,和我们可以重点其中的哪些关键节点,来帮助我们快速定位问题。

二、Android Audio 音频系统 1. 音频链路

抓取音频链路当中的音频数据是分析声音异常问题的有效方法,通过抓取不同节点的声音数据,可以帮助我们快速定位问题发生的原因。下面先来看一张安卓官方的音频系统框架图: 0.005701938452606489.png Audio 音频数据流整体上经过 APP,framework,hal,kernel driver四个部分,从应用端发起,不管调用 audio 还是 media 接口,最终还是会由 AudioTrack 将数据往下传,经由 AudioFlinger 启动 MixThread 或 DirectThread 等,将多个 APP 的声音混合到一起,将声音传输到 hal 层。系统会根据音频流类型 stream 和音频策略 strategy 来选择对应的 output,从而找到对应的 module,将音频数据传输给 hal 层音频库 so 做声音相关的处理并传给 audio driver。 音频流传输路径图: a811ebf2-3b9f-4825-a5a1-22343fafa943.png 从上述的音频流流程可以看到,我们首先要确认,当前音频流是经由哪一个 hal 层库做处理,是 primary,usb 还是三方 so 等,然后可以在对应的节点抓取相应的音频信息分析。可以根据自己的需要在音频流的部分节点埋下相应的 dump 指令,将 pcm 写入到相应的节点当中。

2. 音频链路关键节点:

  AudioTrack:应用写入音频流的起点,有 MODE_STATIC 和 MODE_STREAM 模式,通过 write 接口将数据写入。此节点写入的数据是由应用层最原始的音频数据。   AudioFlinger:负责启动线程完成各个应用的混音,音频流声音调节等工作。设备同时可能存在多个应用播放声音,这时便需要将各个应用的声音混合在一起,并且做音量的调节。例如在车载场景中,音乐应用播放歌曲和地图应用语音导航的声音需要同时存在,便使用到了混音的功能,当导航语音响起时,歌曲声音有一个明显的变小,便可以设置音频流的音量。   audio_hw_hal:hal 层音频处理的入口,为 Android 原生逻辑,各厂家需要按照规范实现其中的音频设置等接口,声明 HAL_MODULE_INFO_SYM 结构体,实现 legacy_adev_open 方法,承接起连接 framework 和 audio driver 的作用,完成一些音效算法等逻辑处理。   AudioStreamOut:和 audio_hw_hal 一样,是Android 给厂家提供的通用类,厂家在实现自己的通用库实现时需要可以按照谷歌规范,然后在相应的音频处理接口中实现自己的对音频流做音效,增益等处理。 audio_hw_hal.cpp 代码如下,不同厂家这里的实现略有差异,这里只截取部分 AOSP 源码。 551bc872-290d-44c5-a4ce-0522d6e77c29.png 37b7d737-ef11-476a-b4d8-ed90a455cf41.png

3. 音频库的选择

从音频流传输路径图可以看到,如何找到是哪一个音频 so 处理声音也是至关重要的。我们知道,系统对于应用层曝光的其实只有通道类型。举个例子:当用户打电话时,可以使用通话通道 STREAM_VOICE_CALL,当用户播放视频时,可以使用媒体通道 STREAM_MUSIC,当发送通知时,可以使用 STREAM_NOTIFICATION。那传入这些通道的声音数据,又是怎么最终流向到具体的硬件输出设备呢? c860785d-d93f-487c-aed9-2f8509b063c6.png 以媒体通道为例,当应用层将音频数据往 MUSIC 通道写入时,系统便会根据 StreamType 来生成相应的 audio_attributes_t(.usage = AUDIO_USAGE_MEDIA, .content_type = AUDIO_CONTENT_TYPE_MUSIC}),再通过 audio_attributes_t 来获取对应的 ProductStrategy(STRATEGY_MEDIA),最后在拿到对应的 outputDevice。Android 原生逻辑outputDevice 的选择在 Engine.cpp 上,会具体根据当前设备是否有接蓝牙,耳机等外设,按照优先级来选择相应的外设设备作为输出,可能是耳机 (AUDIO_DEVICE_OUT_WIRED_HEADSET),听筒(AUDIO_DEVICE_OUT_EARPIECE),喇叭(AUDIO_DEVICE_OUT_SPEAKER)等。具体可以看文件 www.androidos.net.cn/android/9.0… 的 getDeviceForStrategyInt 方法。 通过以上分析,我们知道了音频会流向哪个输出设备,那么下一个问题来了,是由谁负责传输和对音频数据做最后的处理呢? 这里就需要看音频设备的策略文件,还是以媒体通道为例,假设设备没有接任何外接设备,选择的 outputDevice 是 AUDIO_DEVICE_OUT_SPEAKER。接下来就要看哪个 output so 支持 AUDIO_DEVICE_OUT_SPEAKER,符合度最高的 output so 将会负责数据传输,最终经由 tinyalsa 写入到 pcm 节点中。不同的 Android 版本在配置文件上会有些许差异,可能放置在 *audio_policy_configuration.xml 中,有些在 audio_policy.conf 中。

三、案例分析 1. 声音忽大忽小问题 具体分析

有用户反馈使用优酷视频播放视频时,概率性出现声音忽大忽小的问题,一旦出现就是在播放指定音频时是必现的。接下来联系用户帮提供设备的日志信息和操作步骤,按照用户操作来复现问题,通过 demo 还原用户环境参数便能复现。 首先分析确认发现在这个过程中声音音量均无变化,所以初步怀疑可能是和音频流数据出现异常有关。在上图中数字有标识的5个点中分别抓取音频,使用 Audacity 导入音频文件来进行分析,发现位置4的音频正常,而位置5的音频出现了声音异常的现象。具体见下图: 49243c5f-e704-485c-9921-992eb6122469.png 所以便可以确认在音频数据经过 hal 层音效处理前是正常等,经过音效处理后,在某些特殊的声音数据下,音效库缩小了声音的幅值,从而导致声音的异常。为了实锤是音效库 so 导致的问题,通过关闭音效库的功能,最终发现声音忽大忽小的问题消失了。 从以上尝试的结果综合分析可以确认,是音效 so 库对通道声音进行处理时影响到了原有声音的功能。通过修改 so 库最终来解决这个问题。

2. 应用卡顿问题

有用户反馈说是打开应用A播放视频正常,然后直接返回到 home 目录,应用A后台播放时会出现断音的现象。

具体分析

声音卡顿,录音掉帧类的现象在声音问题中非常常见。从现象上来看,就是用户切换到后台时没有暂停播放,视频在后台播放时出现。老规矩,我们先分析相关 log,通过日志分析发现,当问题出现时,日志上频繁打印 get null buffer 的信息。所以怀疑是否是音频数据丢失导致的。dump 音频数据抓取到系统混音后输出到输出设备的原始音频,可以帮助实锤上层系统传下来的数据是否正常。于是发现位置3的音频如下: f6bb3c6f-9ea9-4cf2-bf05-935b896b9d0a.png 从抓取到的音频可以看到,在后台异常播放时,在该时间段内会出现明显音频丢帧的现象。接下来要看看是在哪一块出现了丢帧。进一步分析 audioTrack 传下来的数据,出现了丢失掉一部分音频的现象,时长相当于原音频的一半。基于此,为了实锤是应用A传下来的数据就有缺失,从日志信息跟踪,决定在 audioTrack 上加日志信息来看,发现当切换到后台时,audioTrack 每次还是写 4096 个 byte ,但是写数据的频率降低了一半。   正常:28.600-27.546=1.054 44次 间隔 1.054/43= 0.0245秒/次。   异常:40.839-39.804=1.035 22次 间隔 1.035/21= 0.0493秒/次。 考虑到这一块是否是和后台进程的优先级相关。当进程降低时导致了写数据的线程能够拿到的CPU资源变小,出现了断音的问题。通过和其他型号的平板对比发现,各厂家 Android 10 的平板大部分均有此问题,而 android7 和 android 8 的平板就没有这个问题。基于以上情况,更加怀疑是和 android 的特性相关,可能是新的 android 平板针对后台线程优先级做了处理,目的也很明确,就是限制后台应用的活跃程度,来保证设备性能。此时进一步分析最终发现是和 TimerSlackHigh 的参数相关。 system/core/libprocessgroup/profiles/task_profiles.json:

{ "Name": "TimerSlackHigh", "Actions": [ { "Name": "SetTimerSlack", "Params": { "Slack": "40000000" } } ] }, { "Name": "TimerSlackNormal", "Actions": [ { "Name": "SetTimerSlack", "Params": { "Slack": "50000" } } ] },

该参数影响了应用后台线程 sleep/wait 之间所消耗的时间。可以看到,当应用从前台切换到后台时,这个时间从50 微秒上调到 40 毫秒。从而导致写入音频数据量大大减少。通过修改参数可以解决,但是提高后台线程的活跃度,很可能影响到整体性能,因此不作处理。最终像用户解释,切换后台时可以手动停止播放视频,同时反馈给应用,由应用规范应用流程,起后台进程来做单独处理。

四、总结

按照以上案例,我们来总结下当声音出现异常时一些快速定位调试的手段:

抓取位置1的音频数据,如果该数据异常。代表从应用端传递下来的数据即为异常,大部分情况下为应用问题。曾经遇到一个是应用默认会将 track 音量调为0,此时调节系统音量时不会有声音的。需要用户点击该应用内的一个静音按钮才有声音,这时候就会在位置1抓到一串无声的音频,这种在安卓版本表现是一致的。但是也有可能是像案例2一样和后台优先级有关,导致只在较高的版本上出现问题。 抓取位置3的音频数据,若此音频流经过各播放线程时出现问题,则可能是系统 mix, direct 逻辑出现问题,原生逻辑通常不会有问题,有可能是客制化修改引发的。 抓取位置5的音频数据,此部分逻辑是由 output so 来处理的,可能是音效库处理数据等操作导致声音异常。


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有